/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.eclipse.create_toolbox_project.business;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * Description. this class will create a trampoline project. three
 * under-projects will be create. here are there trees files: C:/project_name/
 * pom.xml - project_name-model/ .classpath pom.xml .project .checkstyle -
 * src/main/ - java - model - java-generated - resources - resources-generated -
 * project_name-src/ .classpath pom.xml .project .checkstyle - main/ -
 * java/project_name/war/plugin/ package.html SpringProviderPlugin.java -
 * resources/ - META-INF/services
 * com.genia.toolbox.spring.provider.plugin.SpringProviderPlugin -
 * project_name/war/plugin main-spring-config.xml - resources-filtered - test/ -
 * java/project_name/war/plugin SpringProviderPluginTest.java - /resources -
 * project_name-war .classpath pom.xml .project .checkstyle -
 * src/main/webapp/WEB-INF/ web.xml
 * 
 * @version 1.0 (2008-06-09)
 */
public class CreateToolboxProject
{

  /**
   * the files to add to the .settings directory.
   */
  private static final String[] SETTINGS_FILES = new String[] { "org.eclipse.core.resources.prefs", "org.eclipse.core.runtime.prefs", "org.maven.ide.eclipse.prefs" };

  /**
   * separator for building tree directories.
   */
  private static final char FILE_SEPARATOR_CHAR = File.separatorChar;

  /**
   * root file for building tree.
   */
  private static final String ROOT_RESOURCES = "/com/genia/toolbox/eclipse/create_toolbox_project/root";

  /**
   * common files.
   */
  private static final String COMMON_RESOURCES = "/com/genia/toolbox/eclipse/create_toolbox_project/common";

  /**
   * Contains path to src_resources.
   */
  private static final String SRC_RESOURCES = "/com/genia/toolbox/eclipse/create_toolbox_project/src";

  /**
   * Contains path to model_resources.
   */
  private static final String MODEL_RESOURCES = "/com/genia/toolbox/eclipse/create_toolbox_project/model";

  /**
   * Contains path to war_resources.
   */
  private static final String WAR_RESOURCES = "/com/genia/toolbox/eclipse/create_toolbox_project/war";

  /**
   * Contains src suffix project.
   */
  public static final String SRC_PROJECT_SUFFIX = "-src";

  /**
   * Contains model suffix project.
   */
  public static final String MODEL_PROJECT_SUFFIX = "-model";

  /**
   * Contains war suffix project.
   */
  public static final String WAR_PROJECT_SUFFIX = "-war";

  /**
   * Id of the project to create.
   */
  private String groupId;

  /**
   * path where the project will be create.
   */
  private String path;

  /**
   * Name of the project.
   */
  private String projectName;

  /**
   * tree of files.
   */
  private String groupIdDir;

  /**
   * parent file of the project.
   */
  private File parent;



  /**
   * Constructor takes two arguments: the Id of the project and the path.
   * 
   * @param groupId
   *          contains name of the project and eventually tree folders names
   * @param path
   *          contains the path where the project will be placed
   */
  public CreateToolboxProject(String groupId, String path)
  {

    // Replace - by _ for package name
    String groupIdReplaced = groupId.replaceAll("-", "_");

    setGroupId(groupIdReplaced);
    setPath(path);

    groupIdDir = getGroupId().replaceAll("\\.", "\\" + String.valueOf(FILE_SEPARATOR_CHAR));

    // search for the last dot index otherwise put zero by default
    final int lastPoint = -1 != getGroupId().lastIndexOf('.') ? 1 + groupId.lastIndexOf('.') : 0;

    // Name of the project
    projectName = groupId.substring(lastPoint);
    // LOGGER.info("each parameters are set");
  }



  /**
   * The method that will run all the necessary methods to create a project.
   * 
   * @throws IOException
   *           if the new file parent thrown an exception
   */

  public void runProject()
      throws IOException
  {

    // Create Hierarchy
    File projectNameFile = new File(getPath(), projectName);
    setParent(projectNameFile);

    // create {project}
    if (!projectNameFile.mkdirs()) {
      throw new IOException("unable to create " + projectNameFile.getCanonicalPath());
    }

    // Copy what we needs in project
    createRootProject();
    createSrcProject();
    createModelProject();
    createWarproject();
  }



  /**
   * Copy file from source to destination.
   * 
   * @param src
   *          indicate where are the file to copy
   * @param dest
   *          indicate where copying this file
   * @throws IOException
   *           if an exception is thrown during the copying process
   */
  private void copyFile(String src, File dest)
      throws IOException
  {
    dest.getParentFile().mkdirs();
    final String[] regex = { "@groupId@", "@groupIdDirSlashed@", "@projectName@" };
    final String[] replacementId = { getGroupId(), getGroupIdDir().replaceAll("\\\\", "/"), // replaceAll
        // in
        // case
        // were
        // separatorChar
        // is
        // not
        // '/'
        getProjectName() };

    if (src == null) {
      throw new IllegalArgumentException("src can not be null.");
    }

    BufferedReader reader = new BufferedReader(new InputStreamReader(CreateToolboxProject.class.getResourceAsStream(src)));

    try {

      BufferedWriter writer = new BufferedWriter(new FileWriter(dest));

      try {

        while (true) {
          String line = reader.readLine();

          if (line == null) {
            break;
          }
          /**
           * this condition replace the variable
           * 
           * @xxx@ in the configuration files
           */
          if (regex != null && replacementId != null) {
            for (int i = 0; i < regex.length; i++)
              line = line.replaceAll(regex[i], replacementId[i]);
          }
          writer.write(line);
          writer.newLine();

        }

      }
      finally {
        writer.close();
      }
    }
    finally {
      reader.close();
    }
  }



  /**
   * Create the root project : {project}.
   * 
   * @throws IOException
   *           if an exception is thrown during the copying process
   */
  private void createRootProject()
      throws IOException
  {

    final String pom = "pom.xml";
    final String pomFile = ROOT_RESOURCES + "/" + pom;
    final File destFile = new File(getParent(), pom);
    copyFile(pomFile, destFile);
  }



  /**
   * Create the first project whose name is {project}/{project-src}.
   * 
   * @throws IOException
   *           if an exception is thrown during the copying process
   */
  private void createSrcProject()
      throws IOException
  {

    // {project}/{project}-src to create
    final File srcProjectFile = new File(getParent(), getParent().getName() + SRC_PROJECT_SUFFIX);

    // {project}/{project}-src/src/main
    final File srcMainDir = new File(new File(srcProjectFile, "src"), "main");

    if (!srcMainDir.mkdirs()) {
      throw new IOException("unable to create " + srcMainDir.getCanonicalPath());
    }

    // {project}/{project}-src/main/java/{groupId}/war
    final File srcProjectMainPlugin = new File(new File(new File(new File(srcMainDir, "java"), getGroupIdDir()), "war"), "plugin");
    // {project}/{project}-src/main/resources
    final File srcProjectMainResources = new File(srcMainDir, "resources");
    // {project}/{project}-src/main/resources/{groupId}/war
    final File srcProjectMainResourcesWar = new File(new File(srcProjectMainResources, getGroupIdDir()), "war");

    // {project}/{project}-src/main/resources/META-INF/services
    final File srcProjectMetaInfServices = new File(new File(srcProjectMainResources, "META-INF"), "services");
    // {project}/{project}-src/main/resources/{groupId}/war/plugin
    final File srcProjectPlugin = new File(srcProjectMainResourcesWar, "plugin");

    // {project}/{project}-src/src/test
    final File srcTestDir = new File(new File(srcProjectFile, "src"), "test");

    // {project}/{project}-src/src/test/java/{groupId/war/plugin}
    final File srcTestJavaDir = new File(new File(new File(new File(srcTestDir, "java"), getGroupIdDir()), "war"), "plugin");

    // {project}/{project}-src/src/test/java/resources
    final File srcTestResourcesDir = new File(srcTestDir, "resources");

    // {project}/{project}-src/src/main/resources-filtered
    final File srcMainResourcesFilteredDir = new File(srcMainDir, "resources-filtered");

    if (!srcProjectMainPlugin.mkdirs()) {
      throw new IOException("unable to create " + srcProjectMainPlugin.getCanonicalPath());
    }
    if (!srcProjectMainResources.mkdirs()) {
      throw new IOException("unable to create " + srcProjectMainResources.getCanonicalPath());
    }
    if (!srcProjectMainResourcesWar.mkdirs()) {
      throw new IOException("unable to create " + srcProjectMainResourcesWar.getCanonicalPath());
    }
    if (!srcProjectMetaInfServices.mkdirs()) {
      throw new IOException("unable to create " + srcProjectMetaInfServices.getCanonicalPath());
    }
    if (!srcProjectPlugin.mkdirs()) {
      throw new IOException("unable to create " + srcProjectPlugin.getCanonicalPath());
    }
    if (!srcTestDir.mkdirs()) {
      throw new IOException("unable to create " + srcTestDir.getCanonicalPath());
    }

    if (!srcTestJavaDir.mkdirs()) {
      throw new IOException("unable to create " + srcTestJavaDir.getCanonicalPath());
    }

    if (!srcTestResourcesDir.mkdirs()) {
      throw new IOException("unable to create " + srcTestResourcesDir.getCanonicalPath());
    }

    if (!srcMainResourcesFilteredDir.mkdirs()) {
      throw new IOException("unable to create " + srcMainResourcesFilteredDir.getCanonicalPath());
    }

    final String serviceFile = "com.genia.toolbox.spring.provider.plugin.SpringProviderPlugin";

    final String pluginFile = SRC_RESOURCES + "/" + serviceFile;
    File destFile = new File(srcProjectMetaInfServices.getCanonicalPath(), serviceFile);

    copyFile(pluginFile, destFile);

    final String springConfig = "main-spring-config.xml";
    final String springConfigFile = SRC_RESOURCES + "/" + springConfig;

    destFile = new File(srcProjectPlugin.getCanonicalPath(), springConfig);
    copyFile(springConfigFile, destFile);

    final String pluginClass = "SpringProviderPlugin.java";

    final String pluginClassFile = SRC_RESOURCES + "/" + pluginClass + ".in";

    destFile = new File(srcProjectMainPlugin.getCanonicalPath(), "SpringProviderPlugin.java");
    copyFile(pluginClassFile, destFile);

    final String htmlFile = SRC_RESOURCES + "/packageFile.html";

    destFile = new File(srcProjectMainPlugin.getCanonicalPath(), "package.html");
    copyFile(htmlFile, destFile);

    String pluginClassTest = "SpringProviderPluginTest.java";
    final String pluginJavaTestFile = SRC_RESOURCES + "/" + pluginClassTest + ".in";
    destFile = new File(srcTestJavaDir.getCanonicalPath(), pluginClassTest);
    copyFile(pluginJavaTestFile, destFile);
    createConfigFiles(SRC_RESOURCES, srcProjectFile);
  }



  /**
   * Create the first project whose name is {project}/{project-model}.
   * 
   * @throws IOException
   *           if an exception is thrown during the copying process
   */
  private void createModelProject()
      throws IOException
  {

    // {project}/{project}-model
    final File modelProjectFile = new File(getParent(), getParent().getName() + MODEL_PROJECT_SUFFIX);
    // {project}/{project}-model/src/main
    final File modelMainDir = new File(new File(modelProjectFile, "src"), "main");
    // {project}/{project}-model/src/main/java
    final File modelProjectMainJava = new File(modelMainDir, "java");
    // {project}/{project}-model/src/main/resources
    final File modelProjectMainResources = new File(modelMainDir, "resources");
    // {project}/{project}-model/src/main/java-generated
    final File modelProjectMainJavaGenerated = new File(modelMainDir, "java-generated");
    // {project}/{project}-model/src/main/resources-generated
    final File modelProjectMainResourcesGenerated = new File(modelMainDir, "resources-generated");
    // {project}/{project}-model/src/main/model
    final File modelProjectMainmodel = new File(modelMainDir, "model");

    if (!modelProjectMainJava.mkdirs()) {
      throw new IOException("unable to create " + modelProjectMainJava.getCanonicalPath());
    }
    if (!modelProjectMainResources.mkdirs()) {
      throw new IOException("unable to create " + modelProjectMainResources.getCanonicalPath());
    }
    if (!modelProjectMainJavaGenerated.mkdirs()) {
      throw new IOException("unable to create " + modelProjectMainJavaGenerated.getCanonicalPath());
    }
    if (!modelProjectMainResourcesGenerated.mkdirs()) {
      throw new IOException("unable to create " + modelProjectMainResourcesGenerated.getCanonicalPath());
    }
    if (!modelProjectMainmodel.mkdirs()) {
      throw new IOException("unable to create " + modelProjectMainmodel.getCanonicalPath());
    }

    createConfigFiles(MODEL_RESOURCES, modelProjectFile);
  }



  /**
   * Create the third project whose name is {project}/{project-war}.
   * 
   * @throws IOException
   *           if an exception is thrown during the copying process
   */
  private void createWarproject()
      throws IOException
  {

    // {project}/{project}-war
    final File warProjectFile = new File(getParent(), getParent().getName() + WAR_PROJECT_SUFFIX);
    // {project}/{project}-war/src/main/webapp/WEB-INF
    final File warWebInfDir = new File(new File(new File(new File(warProjectFile, "src"), "main"), "webapp"), "WEB-INF");

    if (!warWebInfDir.mkdirs()) {
      throw new IOException("unable to create " + warWebInfDir.getCanonicalPath());
    }

    final String web = "web.xml";
    final File destFile = new File(warWebInfDir, web);
    final String webFile = WAR_RESOURCES + "/" + web;
    copyFile(webFile, destFile);

    createConfigFiles(WAR_RESOURCES, warProjectFile);
  }



  /**
   * Copy configuration files for projects. (.classpath, .project, pom.xml,
   * .checkstyle)
   * 
   * @param resourcesDir
   *          directory from where is file
   * @param destDir
   *          directory where to copy the file
   * @throws IOException
   *           if an exception is thrown during the copying process
   */
  private void createConfigFiles(String resourcesDir, File destDir)
      throws IOException
  {

    File destFile;

    String classPath = "classpath";
    destFile = new File(destDir, "." + classPath);
    final String classPathFile = resourcesDir + "/" + classPath;
    copyFile(classPathFile, destFile);

    String project = "project";
    destFile = new File(destDir,  "." + project);
    final String projectFile = resourcesDir + "/" + project;
    copyFile(projectFile, destFile);

    String pom = "pom.xml";
    destFile = new File(destDir, pom);
    final String pomFile = resourcesDir + "/" + pom;
    copyFile(pomFile, destFile);

    createCommonResources(destDir);
  }



  private void createCommonResources(File destDir)
      throws IOException
  {
    File destFile;

    String checkstyle = "checkstyle";
    destFile = new File(destDir, "." + checkstyle);
    final String checkstyleFile = COMMON_RESOURCES + "/" + checkstyle;
    copyFile(checkstyleFile, destFile);

    File settingsDir = new File(destDir, ".settings");
    for (String file : SETTINGS_FILES) {
      copyFile(COMMON_RESOURCES + "/" + file, new File(settingsDir, file));
    }

  }



  /**
   * getter for the groupId property.
   * 
   * @return the groupId
   */
  public String getGroupId()
  {
    return groupId;
  }



  /**
   * setter for the groupId property.
   * 
   * @param groupId
   *          the groupId to set
   */
  public void setGroupId(String groupId)
  {
    this.groupId = groupId;
  }



  /**
   * getter for the path property.
   * 
   * @return the path
   */
  public String getPath()
  {
    return path;
  }



  /**
   * setter for the path property.
   * 
   * @param path
   *          the path to set
   */
  public void setPath(String path)
  {
    this.path = path;
  }



  /**
   * getter for the projectName property.
   * 
   * @return the projectName
   */
  public String getProjectName()
  {
    return projectName;
  }



  /**
   * setter for the projectName property.
   * 
   * @param projectName
   *          the projectName to set
   */
  public void setProjectName(String projectName)
  {
    this.projectName = projectName;
  }



  /**
   * getter for the groupIdDir property.
   * 
   * @return the groupIdDir
   */
  public String getGroupIdDir()
  {
    return groupIdDir;
  }



  /**
   * setter for the groupIdDir property.
   * 
   * @param groupIdDir
   *          the groupIdDir to set
   */
  public void setGroupIdDir(String groupIdDir)
  {
    this.groupIdDir = groupIdDir;
  }



  /**
   * getter for the parent property.
   * 
   * @return the parent
   */
  public File getParent()
  {
    return parent;
  }



  /**
   * setter for the parent property.
   * 
   * @param parent
   *          the parent to set
   */
  public void setParent(File parent)
  {
    this.parent = parent;
  }



  /**
   * main method.
   * 
   * @param args
   *          no comment
   * @throws Exception
   *           throw all kind of exception
   */
  public static void main(String[] args)
      throws Exception
  {

    if (args.length != 2) {
      System.err.println("[program] groupId path");
    }

    String groupId = args[0];
    String path = args[1];

    CreateToolboxProject newProject = new CreateToolboxProject(groupId, path);
    newProject.runProject();

  }

}
